﻿using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Cyss.Core.Repository.EF
{
    /// <summary>
    /// Represents the Entity Framework repository
    /// </summary>
    /// <typeparam name="TEntity">Entity type</typeparam>
    public partial class EfRepository<TEntity> : IRepository<TEntity> where TEntity : BaseEntity, new()
    {
        #region Fields

        /// <summary>
        /// 数据上下文对象
        /// </summary>
        protected virtual IDbContext _context
        {
            set; get;
        }

        private DbSet<TEntity> _entities;

        #endregion

        #region Ctor

        public EfRepository(IDbContext dbContext)
        {
            _context = dbContext;
        }

        #endregion

        #region Utilities

        /// <summary>
        ///回滚实体更改并返回完整错误消息
        /// </summary>
        /// <param name="exception">Exception</param>
        /// <returns>Error message</returns>
        protected string GetFullErrorTextAndRollbackEntityChanges(DbUpdateException exception)
        {
            //rollback entity changes
            if (_context is DbContext dbContext)
            {
                var entries = dbContext.ChangeTracker.Entries()
                    .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified).ToList();

                entries.ForEach(entry => entry.State = EntityState.Unchanged);
            }

            _context.SaveChanges();
            return exception.ToString();
        }

        #endregion

        #region Methods

        /// <summary>
        /// 根据主键值获取实体
        /// </summary>
        /// <param name="id">主键值</param>
        /// <returns></returns>
        public virtual TEntity GetById(object id)
        {
            return Entities.Find(id);
        }

        /// <summary>
        /// 插入一条数据
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="IsSubmit">是否立即提交</param>
        public virtual void Insert(TEntity entity, bool IsSubmit = true)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));

            try
            {
                Entities.Add(entity);
                if (IsSubmit)
                {
                    _context.SaveChanges();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Insert" + Environment.NewLine + entity.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 异步插入一条数据
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        /// <returns></returns>
        public virtual async Task<int> InsertAsync(TEntity entity, bool IsSubmit = true)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));

            try
            {
                Entities.Add(entity);
                if (IsSubmit)
                {
                    return await _context.SaveChangesAsync();
                }
                else
                {
                    return await Task.FromResult(0);
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Insert" + Environment.NewLine + entity.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }


        /// <summary>
        /// 插入多条数据
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        public virtual void Insert(IEnumerable<TEntity> entities, bool IsSubmit = true)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));

            try
            {
                Entities.AddRange(entities);
                if (IsSubmit)
                {
                    _context.SaveChanges();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Inserts" + Environment.NewLine + entities.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 异步插入多条数据
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        /// <returns></returns>
        public virtual async Task<int> InsertAsync(IEnumerable<TEntity> entities, bool IsSubmit = true)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));

            try
            {
                Entities.AddRange(entities);
                if (IsSubmit)
                {
                    return await _context.SaveChangesAsync();
                }
                else
                {
                    return await Task.FromResult(0);
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Inserts" + Environment.NewLine + entities.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 更新一条数据
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        public virtual void Update(TEntity entity, bool IsSubmit = true)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
            try
            {
                //Entities.Update(entity);
                if (IsSubmit)
                {
                    _context.SaveChanges();
                }

            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Update" + Environment.NewLine + entity.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 异步更新一条数据
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        /// <returns></returns>
        public virtual async Task UpdateAsync(TEntity entity, bool IsSubmit = true)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
            try
            {
                Entities.Update(entity);
                if (IsSubmit)
                {
                    await _context.SaveChangesAsync();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Update" + Environment.NewLine + entity.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 更新多条数据
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        public virtual void Update(IEnumerable<TEntity> entities, bool IsSubmit = true)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));
            try
            {
                //Entities.UpdateRange(entities);
                if (IsSubmit)
                {
                    _context.SaveChanges();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Updates" + Environment.NewLine + entities.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 异步更新多条数据
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        /// <returns></returns>
        public virtual async Task UpdateAsync(IEnumerable<TEntity> entities, bool IsSubmit = true)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));
            try
            {
                Entities.UpdateRange(entities);
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                LogSevice.Write("Updates" + Environment.NewLine + entities.ToSerializeObject() + Environment.NewLine + Message, "DbUpdateException");
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 删除一条数据
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        public virtual void Delete(TEntity entity, bool IsSubmit = true)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
            try
            {
                Entities.Remove(entity);
                if (IsSubmit)
                {
                    _context.SaveChanges();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 异步删除一条数据
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        /// <returns></returns>
        public virtual async Task DeleteAsync(TEntity entity, bool IsSubmit = true)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
            try
            {
                Entities.Remove(entity);
                if (IsSubmit)
                {
                    await _context.SaveChangesAsync();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                throw new Exception(Message, exception);
            }
        }


        /// <summary>
        /// 异步删除多条数据
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        public virtual async Task DeleteAsync(IEnumerable<TEntity> entities, bool IsSubmit = true)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));
            try
            {
                Entities.RemoveRange(entities);
                if (IsSubmit)
                {
                    await _context.SaveChangesAsync();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                throw new Exception(Message, exception);
            }
        }


        /// <summary>
        /// 删除多条数据
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="IsSubmit">是否立即提交</param>
        /// <returns></returns>
        public virtual void Delete(IEnumerable<TEntity> entities, bool IsSubmit = true)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));
            try
            {
                Entities.RemoveRange(entities);
                if (IsSubmit)
                {
                    _context.SaveChanges();
                }
            }
            catch (DbUpdateException exception)
            {
                string Message = exception.InnerException != null ? exception.InnerException.Message : exception.Message;
                throw new Exception(Message, exception);
            }
        }

        /// <summary>
        /// 根据表达式删除
        /// </summary>
        /// <param name="predicate"></param>
        public virtual void Delete(Expression<Func<TEntity, bool>> predicate)
        {
            this.TableNoTracking.Where(predicate).ExecuteDelete();
        }

        /// <summary>
        /// 返回修改了的属性集合
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual IEnumerable<IProperty> GetModifiedProperties(TEntity entity)
        {
            var entry = _context.Entrys(entity);
            return entry.CurrentValues.Properties
                 .Where(p => entry.Property(p.Name).IsModified).ToList();
        }

        /// <summary>
        /// 判断属性值是否修改，如果是多个属性都修改才会返回true
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="ProNames"></param>
        /// <returns></returns>
        public virtual bool IsModified(TEntity entity, params string[] ProNames)
        {
            var entry = _context.Entrys(entity);
            foreach (var proName in ProNames)
            {
                if (!entry.Property(proName).IsModified)
                {
                    return false;
                }
            }
            return true;
        }

        #endregion

        #region 异步方法

        #endregion

        #region EF扩展


        public virtual void Delete(int Id)
        {
            _context.Delete<TEntity>(Id);
        }

        public virtual void Delete(IEnumerable<int> Ids)
        {
            _context.Delete<TEntity>(Ids);
        }


        /// <summary>
        /// 大批量更新数据
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="updatePredicate">指定需要更新的字段</param>
        public void BulkUpdate<K>(IEnumerable<TEntity> entities, Expression<Func<TEntity, K>> keySelector = null)
        {
            _context.BulkUpdate<TEntity, K>(entities, string.Empty, keySelector);
        }

        /// <summary>
        /// 大批量插入数据
        /// </summary>
        /// <param name="entities"></param>
        public int BulkInsert(IEnumerable<TEntity> entities, Expression<Func<TEntity, object>> DistinctKeySelector = null)
        {
            if (!entities.Any())
            {
                return 0;
            }
            return _context.BulkInsert<TEntity>(entities, DistinctKeySelector);

        }

        /// <summary>
        /// 大批量插入数据
        /// </summary>
        /// <param name="entities"></param>
        public void BulkInsert(params IEnumerable<BaseEntity>[] entities)
        {
            if (entities.Length <= 0)
            {
                return;
            }
            _context.BulkInsert(null, entities);

        }

        public int ExecuteSqlCommand(string sql)
        {
            return _context.ExecuteSqlCommand(sql);
        }
        public async Task<int> ExecuteSqlCommandAsync(string sql)
        {
            return await _context.ExecuteSqlCommandAsync(sql);
        }
        public IEnumerable<T> Query<T>(string Sql, params SqlParameter[] cmdParms) where T : class, new()
        {
            return null; //return _context.QuerySql<T>(Sql, cmdParms);
        }

        public TQueryableTable TableQueryable<TQueryableTable>()
        {
            throw new NotImplementedException();
        }



        #endregion

        #region Properties

        /// <summary>
        /// 返回一个EF表
        /// </summary>
        public virtual IQueryable<TEntity> Table => Entities;

        /// <summary>
        /// 获取一个启用了“无跟踪”（EF功能）的表。仅当您仅为只读操作加载记录时才使用该表
        /// </summary>
        public virtual IQueryable<TEntity> TableNoTracking => Entities.AsNoTracking();

        /// <summary>
        /// 获取一个实体集
        /// </summary>
        protected virtual DbSet<TEntity> Entities
        {
            get
            {
                if (_entities == null)
                    _entities = _context.Set<TEntity>();

                return _entities;
            }
        }

        #endregion
    }
}
